#include <iostream>

#include "yaml-cpp/yaml.h"

#include "../net/TcpServer.h"
#include "../net/http/HttpServer.h"
#include "../net/http/HttpResponse.h"
#include "../net/http/HttpRequest.h"
#include "../base/log/Logging.h"
#include "../base/log/AsyncLogging.h"
#include "../database/ConnectionPool.h"
#include "../base/FileUtils.h"


#define OUT std::cout
#define END std::endl

/* 关于http请求返回数据格式的数组 */
const std::vector<string> CONTENT_TYPE = {"text/plain", "text/html", "application/javascript", "text/css"};

/* 配置数据库连接池 */
ConnectionPool *pool;

/* 下面是日志相关的配置 */
off_t kRollSize = 500 * 1000 * 1000 / 2;
AsyncLogging *g_asyncLog = nullptr;

void asyncOutput(const char *msg, int len) {
    g_asyncLog->append(msg, len);
}

/**
 * 设置日志库相关内容
 * @param log
 */
void initLog(AsyncLogging *log);

/* 下方是数据库连接池相关配置 */
/**
 * 初始化数据库连接池
 * @param num 链接池链接数量
 */
void initConnectionPool(ConnectionPool *pool_);

/* 下方是关于http服务器的配置 */

//const static std::string HTML_FILE_PATH = "/home/File/htmlFile";
// const static std::string HTML_FILE_PATH = "/home/JavaProject/dist";
const static std::string HTML_FILE_PATH = "../htmlFile";
/**
 * get处理Get请求
 */
void doGet(const HttpRequest &req, HttpResponse *resp);

/**
 * 处理Post请求
 */
void doPost(const HttpRequest &req, HttpResponse *resp);

/**
 * 默认的错误返回处理
 */
void doDefault(const HttpRequest &req, HttpResponse *resp);

/**
 * 处理错误请求
 */
void doFailReq(const HttpRequest &req, HttpResponse *resp);

/**
 * http请求响应分发
 */
void onHttpCallback(const HttpRequest &req, HttpResponse *resp);

/**
 * http服务器启动
 * @param numThreads 要启动的服务线程数量
 */
void startHttpServer(int numThreads);

/* 下方是处理具体的业务逻辑的 */
void login(const HttpRequest &req, HttpResponse *resp);

/* 下方是初始化yaml并读取部分 */
void initFromYaml(){
    YAML::Node config;
    try {
        config = YAML::LoadFile("../config/application.yaml");
        // 读取配置文件
    } catch (YAML::BadFile &e) {
        LOG_FATAL << "读取配置文件失败";
    }

    // 初始化日志部分
    YAML::Node logConfig = config["log"];
    string basename = logConfig["basename"].as<string>();
    off_t kRollSize = logConfig["kRollSize"].as<off_t>();
    int flushInterval = logConfig["flushInterval"].as<int>();
    AsyncLogging log(basename, kRollSize, flushInterval);
    initLog(&log);


    YAML::Node databaseConfig = config["database"];
    int numConnections = databaseConfig["numConnections"].as<int>();
    string database = databaseConfig["databaseName"].as<string>();
    string username = databaseConfig["username"].as<string>();
    string password = databaseConfig["password"].as<string>();
    string host = databaseConfig["host"].as<string>();
    int port = databaseConfig["port"].as<int>();
    ConnectionPool pool_(numConnections,
                         database,
                         username,
                         password,
                         host,
                         port);
    initConnectionPool(&pool_);

    startHttpServer(10);

}

void viewRequest(const HttpRequest & req){
    std::stringstream os;
    os << "request path: " << req.getPath() << std::endl;
    os << "http version: " << req.getVersion() << std::endl;
    os << "http headers: " << std::endl;
    auto headers = req.headers();
    for(const auto & item : headers){
        os << item.first << ":" << item.second << std::endl;
    }
    os << "request body" << std::endl;
    for(const auto & item : req.body()){
        os << item.first << ":" << item.second << std::endl;
    }
}

int main() {
    LOG_INFO << "main() start ~";
    initFromYaml();

    /* std::string basename = "/home/File/logFile/log";
     AsyncLogging log(basename, kRollSize, 1);
     initLog(&log);

     ConnectionPool pool_(12,
                          "Examine",
                          "root",
                          "123456",
                          "localhost",
                          3330);
     initConnectionPool(&pool_);

     startHttpServer(10);*/
}

/* 初始化日志相关 */
void initLog(AsyncLogging *log) {
    // 每 1s 刷盘一次
    Logger::setOutput(asyncOutput);
    Logger::setIsAsync(true);
    Logger::setLogLevel(Logger::LogLevel::DEBUG);
    g_asyncLog = log;
    log->start();
    LOG_INFO << "日志库设置完毕, 异步线程已启动";
}

/* 初始化数据库连接池 */
void initConnectionPool(ConnectionPool *pool_) {
    pool = pool_;
    LOG_INFO << "数据库连接池设置完毕, 已启动数据库连接池";
}

/* http服务器相关 */
void doGet(const HttpRequest &req, HttpResponse *resp) {
    string fileName = HTML_FILE_PATH + req.getPath();
    string file;
    int err_ = FileUtils::readFile(fileName, 102400, &file, NULL);
    if (err_ != 0) {
        // 如果读取失败就调用读取失败的处理
        doDefault(req, resp);
    } else {
        LOG_INFO << "成功请求到文件 : " << fileName;
        resp->setStatusCode(HttpResponse::k200Ok);
        HttpRequest::ContentType type = req.getFileSuffix();
        resp->addHeader("Content-Type", CONTENT_TYPE[type]);
        resp->setStatusMessage("OK");
        resp->setBody(file);
        resp->setCloseConnection(true);
    }
}

void doPost(const HttpRequest &req, HttpResponse *resp) {
    doGet(req, resp);
}

void doDefault(const HttpRequest &req, HttpResponse *resp) {
    string fileName = HTML_FILE_PATH + "/index.html";
    string file;
    int err_ = FileUtils::readFile(fileName, 102400, &file, NULL);
    if (err_ != 0) {
        // 如果读取失败就调用读取失败的处理
        doFailReq(req, resp);
    } else {
        resp->setStatusCode(HttpResponse::k200Ok);
        resp->addHeader("Content-Type", "text/html");
        resp->setStatusMessage("OK");
        resp->setBody(file);
        resp->setCloseConnection(true);
    }
}

void doFailReq(const HttpRequest &req, HttpResponse *resp) {
    resp->setStatusCode(HttpResponse::k400BadRequest);
    resp->addHeader("Content-Type", "text/html");
    resp->setStatusMessage("Bad Request");
    resp->setBody("<!DOCTYPE html>\n"
                  "<html lang=\"ch\">\n"
                  "<head>\n"
                  "<meta charset=\"UTF-8\">\n"
                  "<title>出现400问题啦</title>\n"
                  "</head>\n"
                  "<body>\n"
                  " <div style=\"text-align: center;\">\n"
                  " <h1>请求格式有误 | 服务器不支持, 请检查请求格式</h1>\n"
                  " <h1>400</h1>\n"
                  " </div>\n"
                  "</body>\n"
                  "</html>");
    resp->setCloseConnection(true);
}

void onHttpCallback(const HttpRequest &req, HttpResponse *resp) {
    viewRequest(req);
    switch (req.method()) {
        case HttpRequest::kGet :
            doGet(req, resp);
            break;
        case HttpRequest::kPost :
            doPost(req, resp);
            break;
        default:
            doDefault(req, resp);
    }
}

void startHttpServer(int numThreads) {
    InetAddress serverAddr(8200);
    EventLoop loop;
    HttpServer httpServer(&loop, serverAddr, "LinServer");
    httpServer.setThreadNum(numThreads);
    httpServer.setHttpCallback(onHttpCallback);
    httpServer.start();
    loop.loop();
}

/* 下方业务逻辑 */
void login(const HttpRequest &req, HttpResponse *resp) {
    const std::map<string, string> &body = req.body();
    const auto &it = body.find("username");
    if (it != body.end()) {
        ConnectionPool::Connection conn = pool->getConnection();
        MySQLConnection::fieldsVector res = conn->select("select * from client where username = '" + it->second + "'");
        pool->closeConnection(&conn);
        if (res.size() >= 2) {
            // 查询到用户数据了, 进行正确的用户数据处理
            resp->setStatusCode(HttpResponse::k200Ok);
            resp->addHeader("Content-Type", "text/html");
            resp->setBody(std::string("<!DOCTYPE html>\n"
                                      "<html lang=\"ch\">\n"
                                      "<head>\n"
                                      "<meta charset=\"UTF-8\">\n"
                                      "<title>POST访问</title>\n"
                                      "</head>\n"
                                      "<body>\n"
                                      " <div style=\"text-align: center;\">\n"
                                      " <h1>欢迎登录我的主页</h1>\n"
                                      "<h2>" + res[1][1] + "</h2>\n"
                                                           " </div>\n"
                                                           "</body>\n"
                                                           "</html>"));
            resp->setStatusMessage("OK");
            resp->setCloseConnection(true);
            return;
        }
    }
    doDefault(req, resp);
}

// 性能测试结果 并发量 5k
// 异步日志的性能确实优异
/*
 * wrk -t12 -c4000 -d30s http://localhost:8200/test
 * wrk -t12 -c4000 -d30s http://175.178.51.126:8200/test
Running 30s test @ http://localhost:8200/test
  12 threads and 4000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   108.97ms  232.76ms   2.00s    92.18%
    Req/Sec   477.34    337.48     3.16k    74.06%
  164627 requests in 30.10s, 50.25MB read
  Socket errors: connect 0, read 164627, write 0, timeout 1628
Requests/sec:   5469.77
Transfer/sec:      1.67MB
*/

/*
 * Running 30s test @ http://localhost:8200/login.html
  12 threads and 4000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   100.06ms  222.98ms   1.99s    93.31%
    Req/Sec   491.87    317.15     2.91k    74.44%
  173890 requests in 30.09s, 272.22MB read
  Socket errors: connect 0, read 173890, write 0, timeout 1134
Requests/sec:   5778.37
Transfer/sec:      9.05MB
 poll调用返回的结果
 */

/*
 * Running 30s test @ http://localhost:8200/login.html
  12 threads and 4000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    94.37ms  192.55ms   2.00s    92.76%
    Req/Sec   510.76    340.90     3.53k    75.34%
  175716 requests in 30.09s, 275.06MB read
  Socket errors: connect 0, read 175716, write 1, timeout 768
Requests/sec:   5839.73
Transfer/sec:      9.14MB
 epoll调用返回的结果, 差不多彼此彼此*/